use crate::c_src::{
    osEventFlagsAttr_t, osEventFlagsClear, osEventFlagsDelete, osEventFlagsId_t, osEventFlagsNew,
    osEventFlagsSet, osEventFlagsWait, osFlagsError, osFlagsErrorTimeout, osFlagsWaitAll,
    osFlagsWaitAny, osRtxEventFlags_t,
};

use super::{DynamicMem, Error};

unsafe impl Send for EventFlags {}
unsafe impl Sync for EventFlags {}

const CB_SIZE: usize = size_of::<osRtxEventFlags_t>();

pub struct EventFlags {
    _cb_mem: DynamicMem,
    id: osEventFlagsId_t,
}

impl Drop for EventFlags {
    fn drop(&mut self) {
        unsafe {
            osEventFlagsDelete(self.id);
        }
    }
}

impl EventFlags {
    
    pub fn new2(name: &'static str) -> Result<Self, Error> {
        let cb_mem = DynamicMem::new2(CB_SIZE)?;
        let id = unsafe {
            let attr = osEventFlagsAttr_t {
                name: name.as_ptr() as _,
                attr_bits: 0,
                cb_mem: cb_mem.ptr as _,
                cb_size: CB_SIZE as _,
            };
            osEventFlagsNew(&attr)
        };
        if id.is_null() {
            Err(Error::New)
        } else {
            Ok(Self {
                _cb_mem: cb_mem,
                id,
            })
        }
    }

    pub fn new(name: &'static str) -> Self {
        let cb_mem = DynamicMem::new(CB_SIZE);
        let id = unsafe {
            let attr = osEventFlagsAttr_t {
                name: name.as_ptr() as _,
                attr_bits: 0,
                cb_mem: cb_mem.ptr as _,
                cb_size: CB_SIZE as _,
            };
            osEventFlagsNew(&attr)
        };
        if id.is_null() {
            panic!("event flags new fail");
        }
        Self {
            _cb_mem: cb_mem,
            id,
        }
    }

    pub fn set_flags(&self, flags: u32) -> u32 {
        let ret = unsafe { osEventFlagsSet(self.id, flags) };
        if (ret & osFlagsError) != 0x00 {
            panic!("set flags error");
        }
        ret
    }

    pub fn clear_flags(&self, flags: u32) -> u32 {
        let ret = unsafe { osEventFlagsClear(self.id, flags) };
        if (ret & osFlagsError) != 0x00 {
            panic!("clear flags error");
        }
        ret
    }

    pub fn wait_any_flags(&self, flags: u32, timeout: u32) -> Result<u32, Error> {
        let ret = unsafe { osEventFlagsWait(self.id, flags, osFlagsWaitAny, timeout) };
        if (ret & osFlagsError) != 0x00 {
            if ret == osFlagsErrorTimeout {
                Err(Error::Timeout)
            } else {
                panic!("wait flags error");
            }
        } else {
            Ok(ret)
        }
    }

    pub fn wait_all_flags(&self, flags: u32, timeout: u32) -> Result<u32, Error> {
        let ret = unsafe { osEventFlagsWait(self.id, flags, osFlagsWaitAll, timeout) };
        if (ret & osFlagsError) != 0x00 {
            if ret == osFlagsErrorTimeout {
                Err(Error::Timeout)
            } else {
                panic!("wait flags error");
            }
        } else {
            Ok(ret)
        }
    }
}
